Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature write float32array #106

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

m0ose
Copy link

@m0ose m0ose commented Oct 1, 2019

Added support for float32Array writing. I only tested it on one float32Array but it should work on other typed arrays. Addresses This issue #103.

I tested it with this code.

async function raster_to_tiff(raster) {
  const metadata = {
    height: 32,
    width: 32
  };
  const arrayBuffer = await GeoTIFF.writeArrayBuffer(raster, metadata);
  let blob = new Blob([arrayBuffer], { type: "application/octet-stream;charset=utf-8" });
  var url = (URL.createObjectURL(blob)); // fake_download is just a trick that simulates... you get it.

  return {blob, url}
};
await raster_to_tiff(new Float32Array(32*32))

@@ -252,11 +252,12 @@ const encodeImage = (values, width, height, metadata) => {

const prfx = new Uint8Array(encodeIfds([ifd]));

const img = new Uint8Array(values);
// This should allow other kinds of typed arrays to be used. Uint8 is just a view of the real underlying data.
const img = new Uint8Array(values.buffer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, values is sometimes a simple JavaScript array and not a buffer, so there is no .buffer property available in that case. Here's where this simple array is created: https://github.com/geotiffjs/geotiff.js/blob/master/src/geotiffwriter.js#L320

Copy link
Contributor

@DanielJDufour DanielJDufour Oct 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, on reviewing the code I wrote, I can see it's super confusing and it's not clear what type something is. I'm definitely open to have it being re-written to be more clear as long as people can still pass in a simple array :-)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • When it is a normal js array, does it assume that it is 8 bits?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m0ose yes, that is correct. That is an oversight on my part. My bad. Perhaps, you need to write some code to read metadata.BitsPerSample and metadata.SampleFormat and set what typed array to use based on that? I believe for Float32, BitsPerSample should equal 32 and SampleFormat should equal 3. @constantinius might have insight of the tiff tags, too.

References:

@constantinius
Copy link
Member

Thanks @m0ose for the effort!

Please keep in mind, that apart from writing the array-data several metadata tags have to be set in accordance to the metadata. For example the SampleFormat tag, that tells readers how to interpret the array data.

@m0ose
Copy link
Author

m0ose commented Oct 10, 2019

It now writes metadata. There may be a problem 'though. If the metadata(bitsPerSample and sampleFormat) and the typed array do not match, should it go by whats in the metadata or the typed array?
Right now if they don't match I don't know what will happen. This is related to what @DanielJDufour said up here, #106 (comment).

@constantinius
Copy link
Member

I think this should be solved using a layered architecture.

The high level one should be as easy and uncomplicated as the readRasters and readRGB. The users should not be hassled with the sometimes ugly internals of the TIFF format and the best defaults should used unless understandable options are set. This API will need to be designed, but will not make the cut for v1.0.

The low level API should allow the setting of the various TIFF tags and raster data. Here it could be possible to set conflicting options, but users should usually not access that API directly. I consider the current write API as the lower level one, so you could pick where you get the metadata from and what to do when they are conflicting.

woops. Fixed duplicated float32 check.
@DanielJDufour
Copy link
Contributor

Hi, @m0ose . Let me know when you want me to take another look at your PR! Thanks for your awesome work :-)

@m0ose
Copy link
Author

m0ose commented Oct 17, 2019 via email

@CraigglesO
Copy link

Hey, this might be a unique problem to me? But When I tried your code it didn't work until I enforced big-endian encoding.

So I changed the code like this (SUPER SUPER UGLY, just proof of working):

const data = new Uint8Array(numBytesInIfd + (values.length * 4 * samplesPerPixel));
  times(prfx.length, (i) => { data[i] = prfx[i]; });
  // forEach(img, (value, i) => {
  //   data[numBytesInIfd + i] = value;
  // });

  // store data
  for (let i = 0, vl = values.length; i < vl; i++) {
    const value = values[i]
    const buffer = new ArrayBuffer(4)
    const view = new DataView(buffer)
    view.setFloat32(0, value, false)
    const uint8 = new Uint8Array(view.buffer)
    const idx = numBytesInIfd + i * 4
    data[idx] = uint8[0]
    data[idx + 1] = uint8[1]
    data[idx + 2] = uint8[2]
    data[idx + 3] = uint8[3]
  }

@manivannan23k
Copy link

After trying the above code, I am getting "RangeError: Offset is outside the bounds of the DataView" while parsing the arraybuffer returned.

@manivannan23k
Copy link

Resolved by adding correct "StripByteCounts" value along with "SampleFormat" and "BitsPerSample"

@aboss-developer
Copy link

aboss-developer commented Jul 25, 2022

image

trying to write float32bit the file is corrupted @manivannan23k can you help me.


const len = this.width * this.height;
    const newdata = new Float32Array(len);
    for (var i = 0; i < len; i++) {
      newdata[i] = this.data[i] + this.WhatIfLayers[0]?.data[i];
    }

    const image = await this.originalTiff.getImage();
    const origin = image.getOrigin();
    const resolution = image.getResolution();

    const metadata = {
      height: this.height,
      width: this.width,
      ModelPixelScale: resolution,
      GeographicTypeGeoKey: 3857,
      GeogCitationGeoKey: 'WGS 84',
      GTModelTypeGeoKey: 2,
      ModelTiepoint: [0, 0, 0, ...origin]
    };
    const arrayBuffer = await writeArrayBuffer(newdata, metadata);
    var blob = new Blob([arrayBuffer], { type: 'image/tiff' });
    //uplaodEditedDSM(this.reportid, blob, `dsm_${this.reportid}`)
    var link = document.createElement("a");
    link.href = window.URL.createObjectURL(blob);
    var fileName = 'dsm_edited';
    link.download = fileName;
    link.click();

@benboughton1
Copy link

This would be a fantastic addition. These tricky problems are above my ability but how can I best encourage progress? Is sponsoring the feature an option? It seems like a few people have been interested in this over time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants